/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.apertum.diasled.model.exam;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import ru.apertum.diasled.common.Uses;
import ru.apertum.diasled.forms.FExamination.LayersGetter;
import ru.apertum.diasled.cfg.Configuration;
import ru.apertum.diasled.cfg.MyPoint;
import ru.apertum.diasled.cfg.Outline;
import ru.apertum.diasled.cfg.Zone;
import ru.apertum.diasled.redactor.SensorDraw;
import ru.tri.Pnt;
import ru.tri.Triangle;
import ru.tri.Triangulation;

/**
 *
 * @author Evgeniy Egorov
 */
public class ExamGLEventListener implements GLEventListener, MouseMotionListener, MouseListener, MouseWheelListener {

    final private LayersGetter layersGetter;
    final private GLCanvas canvasGL;

    public LayersGetter getLayersGetter() {
        return layersGetter;
    }
    private ArrayList<Point> butterfly = new ArrayList<>();

    public void setButterfly(ArrayList<Point> butterfly) {
        this.butterfly = butterfly;
    }

    public ExamGLEventListener(GLCanvas canvasGL, LayersGetter layersGetter) {
        this.layersGetter = layersGetter;
        this.canvasGL = canvasGL;
    }
    private float distance = 0;

    public float getDistance() {
        return distance;
    }

    public void setDistance(float distance) {
        this.distance = distance;
    }

    public void setRotateZero() {
        dx = 0;
        dy = 0;
        start = new Point();
        startN = new Point();
        distance = 0;
    }
    /**
     * Конфигурация
     */
    private Configuration configuration = new Configuration();

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;

        // триангуляция  // Initial triangle
        final Triangle initialTriangle = new Triangle(
                new Pnt(-initialSize, -initialSize),
                new Pnt(initialSize, -initialSize),
                new Pnt(0, initialSize));

        dts.clear();
        if (!configuration.getOutlines().isEmpty()) {
            for (Outline outline : configuration.getOutlines()) {
                dts.put(outline, new Triangulation(initialTriangle));
                // в конфигурацию ссенсоров для отрисовки добавим контур, он нулевого давления
                for (MyPoint mpnt : outline.getRoute()) {
                    dts.get(outline).delaunayPlace(new Pnt(mpnt.x, mpnt.y));
                }
            }
            // Отсортируем все сенсоры по контурам
            for (SensorDraw sensor : configuration.getSensors()) {
                double min = 1000000000;
                Outline route = null;
                for (Outline outline : configuration.getOutlines()) {
                    // рисуем ломаную
                    MyPoint first = null;
                    for (MyPoint point : outline.getRoute()) {

                        if (first == null) {
                            first = point;
                        } else {
                            double d = Uses.distancePointToSegment(sensor, first, point);
                            if (d < min) {
                                min = d;
                                route = outline;
                            }
                            first = point;
                        }
                    }
                }
                dts.get(route).delaunayPlace(new Pnt(sensor.x, sensor.y));
            }
        } else {
            Triangulation dt = new Triangulation(initialTriangle);
            for (SensorDraw sensor : configuration.getSensors()) {
                dt.delaunayPlace(new Pnt(sensor.x, sensor.y));
            }
            dts.put(new Outline(), dt);
        }
    }
    private final HashMap<Outline, Triangulation> dts = new HashMap<>();                   // Delaunay triangulation
    private static final int initialSize = 10000;     // Size of initial triangle
    private static final GLU glu = new GLU();

    public void display(GLAutoDrawable gLDrawable) {
        final GL2 gl = gLDrawable.getGL().getGL2();

        gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT);
        gl.glLoadIdentity();
        gl.glTranslatef(0.0f, 0.0f, -4000.f * 1000 / (configuration.max_y - configuration.min_y) + distance); // это подгон по масштабу

        gl.glRotatef(-180f / 220f * (float) dy, 1.0f, 0.0f, 0.0f);
        gl.glRotatef(180, 0.0f, 1.0f, 0.0f);
        gl.glRotatef(180 - 180f / 220f * (float) dx, 0.0f, 0.0f, 1.0f);





        // рисуем индикаторы
        if (getLayersGetter() == null || (getLayersGetter() != null && getLayersGetter().isIndicators())) {
            gl.glBegin(GL.GL_TRIANGLES);
            gl.glColor3f(1.0f, .0f, .0f);
            gl.glVertex3f(0.0f, .0f, 0.0f);
            gl.glVertex3f(0.0f, 15.0f, .0f);
            gl.glVertex3f(0.0f, 0.0f, 200.0f);

            gl.glVertex3f(0.0f, 5.0f, 200.0f);
            gl.glVertex3f(0.0f, 15.0f, .0f);
            gl.glVertex3f(0.0f, 0.0f, 200.0f);

            gl.glVertex3f(10.0f, 0.0f, 200.0f);
            gl.glVertex3f(0.0f, -60.0f, 200.0f);
            gl.glVertex3f(-10.0f, 0.0f, 200.0f);
            gl.glEnd();
        }

        // рисуем бабочку
        if (getLayersGetter() == null || (getLayersGetter() != null && getLayersGetter().isButterfly())) {
            gl.glBegin(GL.GL_LINE_STRIP);
            gl.glColor3f(.8f, .0f, .0f);
            for (Point point : butterfly) {
                gl.glVertex3f(point.x - configuration.center_x, point.y - configuration.center_y, 100);
            }
            gl.glEnd();
        }

        // рисуем контуры
        if (getLayersGetter() == null || (getLayersGetter() != null && getLayersGetter().isOutLine())) {
            // цвер контура
            gl.glColor3f(0.0f, 0.0f, 0.0f);
            // контуры 
            for (Outline route : configuration.getOutlines()) {
                // рисуем ломаную
                gl.glBegin(GL.GL_LINE_LOOP);
                for (MyPoint point : route.getRoute()) {
                    gl.glVertex3f(point.x - configuration.center_x, point.y - configuration.center_y, 1);
                }
                gl.glEnd();
            }
        }

        // зоны в контурах
        if (getLayersGetter() == null || (getLayersGetter() != null && getLayersGetter().isZone())) {
            // цвер контура
            gl.glColor3f(0.0f, 0.0f, 100.0f);
            for (Outline route : configuration.getOutlines()) {

                for (Zone zone : route.getZone()) {
                    // рисуем ломаную
                    gl.glBegin(GL.GL_LINE_STRIP);
                    for (MyPoint point : zone.getRoute()) {
                        gl.glVertex3f(point.x - configuration.center_x, point.y - configuration.center_y, 2);
                    }
                    gl.glEnd();
                }

            }
        }

        // рисуем сенсоры
        if (getLayersGetter() == null || (getLayersGetter() != null && getLayersGetter().isPress())) {
            gl.getGL2().glBegin(GL2.GL_TRIANGLES);
            for (Triangulation dt : dts.values()) {


                for (Iterator<Triangle> itr = dt.iterator(); itr.hasNext();) {
                    final Triangle t = itr.next();
                    // Отсечем особо длинные
                    // если что, то заремачить
                    if (Uses.distancePtoP(new MyPoint((int) t.get(0).coord(0), (int) t.get(0).coord(1)), new MyPoint((int) t.get(1).coord(0), (int) t.get(1).coord(1))) * 4 > configuration.max_x - configuration.min_x
                            || Uses.distancePtoP(new MyPoint((int) t.get(2).coord(0), (int) t.get(2).coord(1)), new MyPoint((int) t.get(1).coord(0), (int) t.get(1).coord(1))) * 4 > configuration.max_x - configuration.min_x
                            || Uses.distancePtoP(new MyPoint((int) t.get(2).coord(0), (int) t.get(2).coord(1)), new MyPoint((int) t.get(1).coord(0), (int) t.get(1).coord(1))) * 4 > configuration.max_x - configuration.min_x) {
                        continue;
                    }
                    boolean f = true;
                    for (Iterator<Pnt> p = t.iterator(); f && p.hasNext();) {
                        final Pnt pnt = p.next();
                        for (int i = 0; i < pnt.dimension(); i++) {
                            if (Math.abs(pnt.coord(i)) == initialSize) {
                                f = false;
                                break;
                            }
                        }
                    }
                    if (!f) {
                        continue;
                    }


                    //gl.getGL2().glBegin(GL.GL_LINE_LOOP);
                    //gl.getGL2().glBegin(GL2.GL_POLYGON);

                    //We want to draw triangle in gradient color
                    //So setting the color of first vertex as red
                    Color color;
                    int bar = configuration.findSensor((int) (t.get(0).coord(0)), (int) (t.get(0).coord(1))).getValue();
                    if (bar == Configuration.NOT_SENS) {
                        color = Color.white;
                        bar = 0;
                    } else {
                        color = configuration.getColorScale().getColor(bar);
                    }
                    gl.getGL2().glColor3f(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f);
                    gl.glVertex3d(t.get(0).coord(0) - configuration.center_x, t.get(0).coord(1) - configuration.center_y,
                            !getLayersGetter().isValume() ? 0 : bar / configuration.getCoefReductionZ());


                    bar = configuration.findSensor((int) (t.get(1).coord(0)), (int) (t.get(1).coord(1))).getValue();
                    if (bar == Configuration.NOT_SENS) {
                        color = Color.white;
                        bar = 0;
                    } else {
                        color = configuration.getColorScale().getColor(bar);
                    }
                    //Setting the color of second vertex as green
                    gl.getGL2().glColor3f(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f);
                    gl.glVertex3d(t.get(1).coord(0) - configuration.center_x, t.get(1).coord(1) - configuration.center_y,
                            !getLayersGetter().isValume() ? 0 : bar / configuration.getCoefReductionZ());


                    bar = configuration.findSensor((int) (t.get(2).coord(0)), (int) (t.get(2).coord(1))).getValue();
                    if (bar == Configuration.NOT_SENS) {
                        color = Color.white;
                        bar = 0;
                    } else {
                        color = configuration.getColorScale().getColor(bar);
                    }
                    //Setting the color of third vertex as blue
                    gl.getGL2().glColor3f(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f);
                    gl.glVertex3d(t.get(2).coord(0) - configuration.center_x, t.get(2).coord(1) - configuration.center_y,
                            !getLayersGetter().isValume() ? 0 : bar / configuration.getCoefReductionZ());
                    //Our polygon ends here	
                    //gl.getGL2().glEnd();
                }
            }
            //Our polygon ends here	
            gl.getGL2().glEnd();
        }


    }

    public void displayChanged(GLAutoDrawable gLDrawable,
            boolean modeChanged, boolean deviceChanged) {
    }

    public void init(GLAutoDrawable gLDrawable) {
        final GL2 gl = gLDrawable.getGL().getGL2();
        gl.glShadeModel(GL2.GL_SMOOTH);
        gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        //gl.glClearDepth(1.0f);
        gl.glClearDepth(0.0f);
        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthFunc(GL.GL_GREATER);

        //   gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        //   gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
    }

    public void reshape(GLAutoDrawable gLDrawable, int x, int y, int width, int height) {
        final GL2 gl = gLDrawable.getGL().getGL2();
        if (height <= 0) {
            height = 1;
        }
        final float h = (float) width / (float) height;
        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(10.0f, h, .0, 150);

        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    @Override
    public void dispose(GLAutoDrawable glad) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (start != null && startN != null) {
            dx = startN.x + e.getX() - start.x;
            dy = startN.y + e.getY() - start.y;
            java.awt.EventQueue.invokeLater(new Runnable() {

                public void run() {
                    canvasGL.repaint();
                }
            });
        }
    }
    int dx, dy;

    @Override
    public void mouseMoved(MouseEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        startN = new Point(dx, dy);
        start = e.getPoint();
        canvasGL.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    }
    private Point start;
    private Point startN;

    @Override
    public void mouseReleased(MouseEvent e) {
        canvasGL.setCursor(Cursor.getDefaultCursor());
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
    float pos = 0;

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
        pos = pos + e.getWheelRotation() * 50;
        setDistance(pos);
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                canvasGL.repaint();
            }
        });
    }
}
